{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基本知识"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 灰度图像的存储方式：\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzpudsb595j20gc05u0uk.jpg)\n",
    "- 多通道图像存储方式\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzpueh59f1j20ln05cgno.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OpenCV 中的通道存储为 BGR"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 像素值的存储方式\n",
    "- RGB 模式，显示设备采用这种模式\n",
    "- HSV、HLS 将颜色分解成色调、饱和度和亮度/明度\n",
    "- YCrCb 在 JPEG 图像格式中广泛使用\n",
    "- CIE L*a*b* 是一种感知上均匀的颜色空间，它适用来度量两个颜色之间的距离"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 图片的基本操作\n",
    "学习目标：\n",
    "- 访问像素值并修改\n",
    "- 访问图片属性\n",
    "- 设置图像区域（ROI）\n",
    "- 拆分、合并图像\n",
    "\n",
    "这一节主要大部分涉及 Numpy 库的使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 访问并且修改像素值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:05.815903Z",
     "start_time": "2019-02-25T08:56:05.706965Z"
    }
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:08.792798Z",
     "start_time": "2019-02-25T08:56:08.766810Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(150, 220, 3)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 加载一个彩色图\n",
    "img = cv2.imread(\"img.jpg\")\n",
    "img.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:09.684526Z",
     "start_time": "2019-02-25T08:56:09.678567Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 85 180 173]\n",
      "85\n"
     ]
    }
   ],
   "source": [
    "px = img[100, 100]\n",
    "print(px)\n",
    "blue = img[100, 100, 0]\n",
    "print(blue)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:10.324231Z",
     "start_time": "2019-02-25T08:56:10.316234Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[255 255 255]\n"
     ]
    }
   ],
   "source": [
    "# 修改特定的像素\n",
    "img[100, 100] = [255, 255, 255]\n",
    "print(img[100, 100])\n",
    "\n",
    "# 这种修改每个像素的做法效率很低"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**最好使用下面这种方法**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:11.574914Z",
     "start_time": "2019-02-25T08:56:11.567919Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "100"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 获取 RED 通道值\n",
    "img.item(10, 10, 1)\n",
    "\n",
    "# 修改\n",
    "img.itemset((10, 10, 2), 100)\n",
    "img.item(10, 10, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 颜色空间缩减"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "若是单通道的像素，像素有 256（0-255）个值，若是三通道，则颜色数就更多（一千六百多万种），如此多的颜色进行处理，会对算法的性能造成影响。这些颜色中，有代表性的颜色只是小部分。\n",
    "\n",
    "颜色空间缩减（color space reduction）可以大大降低运算复杂度，具体做法是：\n",
    "- 0-9 范围的像素值为 0；\n",
    "- 10-19 范围像素值为 10；\n",
    "- 以此类推\n",
    "\n",
    "算法实现步骤：\n",
    "1. 遍历图像矩阵的每个像素\n",
    "2. 根据公式：![](https://ws1.sinaimg.cn/large/acbcfa39gy1g1dwm4gr7kj204f00j741.jpg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:18.273453Z",
     "start_time": "2019-02-25T08:56:13.460939Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([ 21,  26,  28,  33,  34,  35,  37,  38,  39,  40,  41,  42,  43,\n",
       "         44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,\n",
       "         57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,\n",
       "         70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,\n",
       "         83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,\n",
       "         96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108,\n",
       "        109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,\n",
       "        122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,\n",
       "        135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,\n",
       "        148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,\n",
       "        161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,\n",
       "        174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,\n",
       "        187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,\n",
       "        200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,\n",
       "        213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,\n",
       "        226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,\n",
       "        239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,\n",
       "        252, 253, 254, 255], dtype=uint8),\n",
       " array([ 20,  30,  40,  50,  60,  70,  80,  90, 100, 110, 120, 130, 140,\n",
       "        150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250], dtype=uint8))"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "img = cv2.imread(\"img.jpg\", 0) # img.shape = 150*220\n",
    "img_new = np.array([i for i in map(lambda x:(x//10)*10, img)])\n",
    "\n",
    "cv2.imshow(\"img\", img)\n",
    "cv2.imshow(\"img_new\", img_new)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n",
    "\n",
    "# 统计不同元素的个数\n",
    "np.unique(img),np.unique(img_new)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果，从肉眼来看，差别不大\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzq97b90gzj20ck052759.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 获取图像的属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:20.804497Z",
     "start_time": "2019-02-25T08:56:20.799502Z"
    }
   },
   "outputs": [],
   "source": [
    "img;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:21.422218Z",
     "start_time": "2019-02-25T08:56:21.416221Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(150, 220)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "img.shape\n",
    "# 若是彩色图像，则返回元组 1*3\n",
    "# 若是灰色图像，则返回元组 1*2 只有行和列\n",
    "# 此方法可以用来判断是否为彩色图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:22.093831Z",
     "start_time": "2019-02-25T08:56:22.086835Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "33000"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 所有像素的总和即上面的元组值相乘\n",
    "img.size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:22.667503Z",
     "start_time": "2019-02-25T08:56:22.658509Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('uint8')"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 图像的数据类型\n",
    "img.dtype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**img.dtype 在调试时非常重要，因为大部分错误是因为无效的数据类型引起的**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 感兴趣区域（ROI）\n",
    "该部分的功能是对图像的一小部分区域进行处理（我们感兴趣的那部分），可以减少处理时间，增加精度，给图像处理带来便利。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:26.354392Z",
     "start_time": "2019-02-25T08:56:24.977182Z"
    }
   },
   "outputs": [],
   "source": [
    "img = cv2.imread(\"messi5.jpg\")\n",
    "cv2.imshow(\"image_init\", img)\n",
    "ball = img[280:340, 330:390]\n",
    "img[273:333, 100:160] = ball\n",
    "cv2.imshow(\"image_roi\", img)\n",
    "k = cv2.waitKey(0) & 0xFF\n",
    "if k == 27:\n",
    "    cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本例是对找到图像中球的位置，并复制一个放在另外一个地方，注意看的话，你会发现复制的球是一个矩形，看起来并不协调？\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzlms00px6j20uu0aagw9.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 拆分、合并图像通道"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:28.519213Z",
     "start_time": "2019-02-25T08:56:28.512215Z"
    }
   },
   "outputs": [],
   "source": [
    "# 拆分每个通道\n",
    "b,g,r = cv2.split(img) # 或者 b = img[:, :, 0]\n",
    "# 把各个通道合并起来\n",
    "img = cv2.merge((b,g,r))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:29.354909Z",
     "start_time": "2019-02-25T08:56:29.348913Z"
    }
   },
   "outputs": [],
   "source": [
    "# 让红色通道置零，可以不用拆分红色通道，直接置零\n",
    "img[:, :, 2] = 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`cv2.split()` 对系统来说开销很大，所以只在需要使用的时候再使用，使用 Numpy 索引的方法更有效"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 为图像创建边框(填充)\n",
    "`cv2.copyMakeBorder()`: 可以为图像创建边框它在 **卷积运算，零填充**等方面有更多的应用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:56:38.392463Z",
     "start_time": "2019-02-25T08:56:37.632898Z"
    },
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "BLUE = [255, 0, 0]\n",
    "\n",
    "img1 = cv2.imread('opencv-logo.png')\n",
    "\n",
    "replicate = cv2.copyMakeBorder(\n",
    "    img1, 10, 10, 10, 10, cv2.BORDER_REPLICATE)  # 最后一个元素的复制\n",
    "reflect = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_REFLECT)  # 边框的镜面\n",
    "reflect101 = cv2.copyMakeBorder(\n",
    "    img1, 10, 10, 10, 10, cv2.BORDER_REFLECT_101)  # 同上，但有细微变化\n",
    "wrap = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_WRAP)\n",
    "# 添加一个固定颜色的边框,因为 matpltlib 和 opencv 颜色显示不一样，所有图标的红色和蓝色互换了\n",
    "constant = cv2.copyMakeBorder(\n",
    "    img1, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=BLUE)\n",
    "\n",
    "\n",
    "plt.subplot(231), plt.imshow(img1, 'gray'), plt.title('ORIGINAL')\n",
    "plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')\n",
    "plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')\n",
    "plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')\n",
    "plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')\n",
    "plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://ws1.sinaimg.cn/large/acbcfa39ly1g0n8yz2n31j20ap072tal.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数说明\n",
    "\n",
    "`copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]]) -> dst`\n",
    "- src: 输入图片\n",
    "- top, bottom, left, right: 上下左右的宽度\n",
    "- borderType: 边框类型，多个可以选择 [参见]\n",
    "(https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_core/py_basic_ops/py_basic_ops.html#basic-ops)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-01-30T06:45:40.177950Z",
     "start_time": "2019-01-30T06:45:40.171953Z"
    }
   },
   "source": [
    "#### 更多资料\n",
    "[本节原文]\n",
    "(https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_core/py_basic_ops/py_basic_ops.html#basic-ops)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 图像对比度、亮度值调整\n",
    "学习目标：\n",
    "- 用 OpenCV 进行图像对比度、亮度的动态调整"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 理论依据\n",
    "一般的图像处理算子都是一个函数，它接受一个或多个输入图像，并产生输出图像。算子的一般形式：$g(x) = h(f(x))$ 或者 ![](https://ws1.sinaimg.cn/large/acbcfa39gy1g1dwnkybn7j204i00i741.jpg)\n",
    "图像亮度和对比度的调整操作，属于图像操作中的**点操作**。点操作的特点：仅仅根据输入像素值（有时可加上某些全局信息或参数），来计算相应的输出像素值。这类算子包括：亮度（brightness）、对比度（contrast）调整、颜色校正（colorcorrection）、变换（transformations）。\n",
    "\n",
    "常见的点操作（或说点算子）是乘上一个常数（对应对比度发调节）和加上一个常数（对应亮度的调节）。公式为：$g(x)=a*f(x)+b$\n",
    "各个参数的含义：\n",
    "- 参数 f(x) 表示源图像像素\n",
    "- 参数 g(x) 表示输出图像像素\n",
    "- 参数 a （需要满足 a>0 ）被称为增益（gain），控制图像的对比度\n",
    "- 参数 b 称为偏置（bias），控制图像的亮度\n",
    "\n",
    "公式改写：$g(i, j)=a*f(i, j)+b$\n",
    "即对图像的 i 行 j 列的像素进行操作。（对所有的像素进行操作）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 案例（利用轨迹条调节图像的亮度和对比度）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:23.171334Z",
     "start_time": "2019-02-25T08:57:13.783648Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "from skimage import img_as_int\n",
    "\n",
    "\n",
    "def nothing(x):\n",
    "    pass\n",
    "\n",
    "# 对比度和亮度调节函数\n",
    "def adjust_bright_contrast(img, a=1, b=0):\n",
    "    new_img = np.zeros(img.shape, dtype=img.dtype)\n",
    "    for b in range(img.shape[0]):\n",
    "        for g in range(img.shape[1]):\n",
    "            for r in range(img.shape[2]):\n",
    "                new_img[b, g, r] = np.clip(a*img[b, g, r]+b, 0, 255)\n",
    "    return new_img\n",
    "\n",
    "# 使用轨迹条应该先创建一个窗口，因为\n",
    "cv2.namedWindow(\"image\") \n",
    "cv2.createTrackbar(\"bright\", \"image\", 0, 300, nothing)\n",
    "cv2.createTrackbar(\"contrast\", \"image\", 0, 255, nothing)\n",
    "\n",
    "\n",
    "img = cv2.imread(\"img.jpg\")\n",
    "new_img = np.zeros(img.shape, dtype=img.dtype)\n",
    "\n",
    "while(1):\n",
    "    \n",
    "    cv2.imshow(\"image\", new_img)\n",
    "    k = cv2.waitKey(1) & 0xFF\n",
    "    if k == 27:\n",
    "        break\n",
    "    brightness = cv2.getTrackbarPos(\"bright\", \"image\")\n",
    "    contrast = cv2.getTrackbarPos(\"contrast\", \"image\")\n",
    "    new_img[:] = adjust_bright_contrast(img, brightness*0.01, contrast)\n",
    "    \n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果：![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzr0blkcm3j211t08g3zi.jpg)\n",
    "这个方式运行起来很慢，因为里面有三层循环"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 离散傅立叶变换"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "待学习"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 图像的算术运算\n",
    "学习目标：\n",
    "- 图像的加法、减法、位运算\n",
    "- cv2.add(), cv2.addWeighted()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 图像加法\n",
    "`cv2.add()` 使用该函数操作是 Numpy 操作，两个图片应该要有一样的数据类型和深度，否则第二个图像只能是标量值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:27.493790Z",
     "start_time": "2019-02-25T08:57:27.435827Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "opencv add operation: [[255]]\n",
      "opencv sub operation: [[0]]\n",
      "numpy add operation: [4]\n",
      "numpy sub operation: [251]\n"
     ]
    }
   ],
   "source": [
    "x = np.uint8([250])\n",
    "y = np.uint8([10])\n",
    "z = np.uint8([255])\n",
    "print(\"opencv add operation:\", cv2.add(x, y)) # 250+10 = 260 => 255 \n",
    "print(\"opencv sub operation:\", cv2.subtract(x, z)) # 250-255 = -5 => 0\n",
    "\n",
    "print(\"numpy add operation:\", x+y) # 250+10 = 260 % 256 = 4\n",
    "print(\"numpy sub operation:\", x-z) # 250-255 = -5 % 256 = 251"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OpenCV 运算和 Numpy 运算有区别：OpenCV 是饱和运算，即相加最大只能是 255 ,相减最小只能是 0。Numpy 是模运算。见上面注释。\n",
    "\n",
    "**最好使用 OpenCV 中的 add 进行运算**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 图像融合\n",
    "`cv2.addWeighted()` 两张图片以权重进行融合，使其给人一种混合或透明的感觉。图片按以下公式运算\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1g1dwovnoqrj206900idfl.jpg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:30.831736Z",
     "start_time": "2019-02-25T08:57:29.696387Z"
    }
   },
   "outputs": [],
   "source": [
    "img1 = cv2.imread(\"ml.png\")\n",
    "img2 = cv2.imread(\"opencv-logo.png\")\n",
    "\n",
    "dst = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)\n",
    "cv2.imshow('dst', dst)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`addWeighted` 运算公式为： $dst = \\alpha*img1 + \\beta*img2 + \\gamma$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzonysvg4zj20cz069q60.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "轨迹条版本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-01T05:54:14.020271Z",
     "start_time": "2019-03-01T05:54:03.998014Z"
    }
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "img1 = cv2.imread(\"ml.png\")\n",
    "img2 = cv2.imread(\"opencv-logo.png\")\n",
    "\n",
    "def nothing(x):\n",
    "    pass\n",
    "\n",
    "cv2.namedWindow(\"image\")\n",
    "cv2.createTrackbar(\"alpha\", \"image\", 0, 100, nothing)\n",
    "dst = np.zeros(img1.shape, dtype=img1.dtype)\n",
    "\n",
    "while(1):\n",
    "    cv2.imshow('image', dst)\n",
    "    alpha  = cv2.getTrackbarPos(\"alpha\", \"image\")\n",
    "    dst = cv2.addWeighted(img1, alpha*0.01, img2, 1 -alpha*0.01, 0)\n",
    "    k = cv2.waitKey(1) & 0xFF\n",
    "    if k == 27:\n",
    "        break\n",
    "    \n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 位运算\n",
    "包括 AND、OR、NOT 和 XOR 操作。它们在提取图像的任何部分、定义和处理非矩形 ROI 时非常有用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### AND 运算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:37.934374Z",
     "start_time": "2019-02-25T08:57:35.759475Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[0]], dtype=uint8), array([[10]], dtype=uint8))"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 画矩形\n",
    "Rectangle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)\n",
    "cv2.imshow(\"Rectangle\", Rectangle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "# 画圆形\n",
    "Circle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.circle(Circle, (150, 150), 150, 255, -1)\n",
    "cv2.imshow(\"Circle\", Circle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "bit_and = cv2.bitwise_and(Rectangle, Circle)\n",
    "cv2.imshow(\"bit_and\", bit_and)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "\"\"\"\n",
    "sub = cv2.subtract(Rectangle, Circle)\n",
    "cv2.imshow(\"sub\", sub)\n",
    "cv2.waitKey()\n",
    "\"\"\"\n",
    "\n",
    "cv2.destroyAllWindows()\n",
    "\n",
    "x = np.uint8([10])\n",
    "y = np.uint8([20])\n",
    "z = np.uint8([10])\n",
    "\n",
    "cv2.bitwise_and(x, y), cv2.bitwise_and(x, z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzp33zb24wj20pi09cq34.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### OR 运算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:41.904143Z",
     "start_time": "2019-02-25T08:57:39.602420Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[30]]\n",
      "[[42]]\n"
     ]
    }
   ],
   "source": [
    "# 画矩形\n",
    "Rectangle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)\n",
    "cv2.imshow(\"Rectangle\", Rectangle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "# 画圆形\n",
    "Circle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.circle(Circle, (150, 150), 150, 255, -1)\n",
    "cv2.imshow(\"Circle\", Circle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "bit_or = cv2.bitwise_or(Rectangle, Circle)\n",
    "cv2.imshow(\"bit_or\", bit_or)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "\n",
    "add = cv2.add(Rectangle, Circle)\n",
    "cv2.imshow(\"add\", add) \n",
    "cv2.waitKey(0)\n",
    "\n",
    "cv2.destroyAllWindows()\n",
    "x = np.uint8([10]) # 0000 1010\n",
    "y = np.uint8([20]) # 0001 0100 \n",
    "z = np.uint8([40]) # 0010 1000\n",
    "\n",
    "print(cv2.bitwise_or(x, y)) # 0001 1110 => 30\n",
    "print(cv2.bitwise_or(x, z)) # 0010 1010 => 42"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**OR 运算和 `cv2.add()` 结果一致?**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzpm7yby2uj20xv09574j.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### XOR（异或）运算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:46.638491Z",
     "start_time": "2019-02-25T08:57:44.768562Z"
    }
   },
   "outputs": [],
   "source": [
    "# 画矩形\n",
    "Rectangle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)\n",
    "cv2.imshow(\"Rectangle\", Rectangle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "# 画圆形\n",
    "Circle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.circle(Circle, (150, 150), 150, 255, -1)\n",
    "cv2.imshow(\"Circle\", Circle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "bit_xor = cv2.bitwise_xor(Rectangle, Circle)\n",
    "cv2.imshow(\"bit_xor\", bit_xor)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzp3acatsgj20pl09egls.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### NOT 运算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:50.572277Z",
     "start_time": "2019-02-25T08:57:48.847230Z"
    }
   },
   "outputs": [],
   "source": [
    "# 画矩形\n",
    "Rectangle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)\n",
    "cv2.imshow(\"Rectangle\", Rectangle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "# 画圆形\n",
    "Circle = np.zeros((300, 300), dtype=\"uint8\")\n",
    "cv2.circle(Circle, (150, 150), 150, 255, -1)\n",
    "cv2.imshow(\"Circle\", Circle)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "bit_not = cv2.bitwise_not(Rectangle, Circle)\n",
    "cv2.imshow(\"bit_not\", bit_not)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzp3citxwrj20pn09gwem.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 综合例程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "mask：图像掩模"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:57:57.568393Z",
     "start_time": "2019-02-25T08:57:52.841980Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(342, 548, 3) (186, 152, 3)\n",
      "roi.shape: (186, 152, 3)\n",
      "ret 25.0\n"
     ]
    }
   ],
   "source": [
    "# 加载图片\n",
    "img1 = cv2.imread(\"messi5.jpg\")\n",
    "img2 = cv2.imread(\"opencv-logo.png\")\n",
    "\n",
    "print(img1.shape, img2.shape)\n",
    "\n",
    "# 把 OpenCV logo 放在图片的左上角，创建一个 ROI 大小和 logo 图像一致\n",
    "rows, cols, channels = img2.shape\n",
    "roi = img1[0:rows, 0:cols]\n",
    "print(\"roi.shape:\", roi.shape)\n",
    "cv2.imshow(\"roi\", roi)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "# 灰度图\n",
    "img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)\n",
    "cv2.imshow(\"img2gray\", img2gray)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "ret, mask = cv2.threshold(img2gray, 25, 255, cv2.THRESH_BINARY)\n",
    "# 解释：若图片 im2gray 的像素值小于 25，则像素置 0（黑），否则置 255（白）\n",
    "# ret ：得到的阈值，mask：阈值化后的图像\n",
    "print(\"ret\", ret)\n",
    "cv2.imshow(\"mask\", mask)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "mask_inv = cv2.bitwise_not(mask)\n",
    "# 把 mask 图像的白色像素变为黑色，黑色像素变为白色\n",
    "cv2.imshow(\"mask_inv\", mask_inv)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "\n",
    "# 将 img1 中的 ROI 的 logo 区域涂黑\n",
    "img1_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)\n",
    "cv2.imshow(\"img1_bg\", img1_bg)\n",
    "cv2.waitKey(0)\n",
    "# plt.imshow(img1_bg)\n",
    "\n",
    "# 只提取 logo 图像中的 logo\n",
    "img2_fg = cv2.bitwise_and(img2, img2, mask=mask)\n",
    "cv2.imshow(\"img2_fg\", img2_fg)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "\n",
    "# 将 ROI 区域与 logo 图标运算\n",
    "dst = cv2.add(img1_bg, img2_fg)\n",
    "cv2.imshow(\"dst\", dst)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "\n",
    "img1[0:rows, 0:cols] = dst\n",
    "\n",
    "cv2.imshow(\"res\", img1)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1fzpvb58iwnj20q20gv16o.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-01-31T08:46:36.289454Z",
     "start_time": "2019-01-31T08:46:26.495079Z"
    }
   },
   "source": [
    "要将两张图放在一起，且都是原来的图像，如上图所示，将 OpenCV logo （img2）放在 img1 上面，巧妙的利用黑色像素（0）。\n",
    "如何将两个图一原来的图像进行重叠：\n",
    "1. 创建一个 ROI 区域（ROI 区域来自底图，如上面的例子的 img1）\n",
    "2. 将 logo 之外的像素置 0 （就是背景是纯黑色），如上图的 img2_fg\n",
    "3. 将 ROI 区域上要放置 logo 的位置置 0，如上图的 img1_bg\n",
    "4. 将 2、3 步得到的图像相加（ cv2.add() ）\n",
    "5. 将第 4 步得到的图像将 img1 的区域替换掉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 练习（用函数实现图像的无缝拼接）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:02.126824Z",
     "start_time": "2019-02-25T08:58:01.010482Z"
    }
   },
   "outputs": [],
   "source": [
    "def pinjie(img1, img2, x=0, y=0):\n",
    "    \"\"\"\n",
    "    img1: 底图\n",
    "    img2: 上方的图\n",
    "    x, y:选择放置的位置\n",
    "    \"\"\"\n",
    "    # 创建一个 ROI \n",
    "    rows, cols, channel = img2.shape\n",
    "    roi = img1[x:x+rows, y:y+cols]\n",
    "    img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)\n",
    "    ret, mask = cv2.threshold(img2gray, 25, 255, cv2.THRESH_BINARY)\n",
    "    mask_inv = cv2.bitwise_not(mask)    \n",
    "    roi = cv2.bitwise_and(roi, roi, mask=mask_inv) \n",
    "    img2_bg = cv2.bitwise_and(img2, img2, mask=mask)\n",
    "    img1[x:x+rows, y:y+cols] = cv2.add(roi, img2_bg)\n",
    "    return img1\n",
    "\n",
    "\n",
    "img1 = cv2.imread(\"messi5.jpg\")\n",
    "img2 = cv2.imread(\"opencv-logo.png\")\n",
    "dst = pinjie(img1, img2, 100, 100)\n",
    "cv2.imshow(\"dst\", dst)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-01-31T09:07:48.069413Z",
     "start_time": "2019-01-31T09:07:48.064420Z"
    }
   },
   "source": [
    "#### 更多资料\n",
    "[本节原文]\n",
    "(https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_core/py_image_arithmetics/py_image_arithmetics.html) \n",
    "\n",
    "[Python下opencv使用笔记（四）（图像的阈值处理）]\n",
    "(https://blog.csdn.net/on2way/article/details/46812121)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 性能评估和改进技术\n",
    "图像处理中，不仅需要准确，更需要快速的方法进行处理。\n",
    "学习目标：\n",
    "- 评估代码的性能\n",
    "- 提升代码性能的小技巧\n",
    "- cv2.getTickCount, cv2.getTickFrequency"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`time` 模块可以测量执行时间；\n",
    "`profile` 模块有助于获得关于代码的详细报告，比如代码中每个函数花费了多少时间、调用了多少次等等。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 使用 OpenCV 衡量性能\n",
    "- `cv2.getTickCount ` 在代码前后使用可以得到代码的运行时间\n",
    "- `cv2.getTickFrequency` 返回时钟周期的频率，即每秒的时钟周期数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:08.646867Z",
     "start_time": "2019-02-25T08:58:07.799349Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(8315303, 0.8315303)"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "img1 = cv2.imread('messi5.jpg')\n",
    "\n",
    "e1 = cv2.getTickCount()\n",
    "for i in range(5,49,2):\n",
    "    img1 = cv2.medianBlur(img1,i)\n",
    "e2 = cv2.getTickCount()\n",
    "t = (e2 - e1)/cv2.getTickFrequency()\n",
    "e2 - e1, t"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "python 中的 `time` 模块也可以实现该功能"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### OpenCV 中的默认优化\n",
    "OpenCV 的许多函数都是使用SSE2、AVX等优化的。它还包含未优化的代码。因此，如果我们的系统支持这些特性，我们应该利用它们(几乎所有现代处理器都支持它们)。它在编译时默认启用。OpenCV 在启用时运行优化后的代码，否则运行未优化的代码。使用 `cv2.useoptimization()`来检查它是否启用/禁用，使用\n",
    "`cv2.setuseoptimization()`来启用/禁用它。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:12.641970Z",
     "start_time": "2019-02-25T08:58:12.535031Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 检查优化器是否开启\n",
    "cv2.setUseOptimized(True)\n",
    "cv2.useOptimized()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:15.655243Z",
     "start_time": "2019-02-25T08:58:13.032783Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "31.8 ms ± 1.28 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    }
   ],
   "source": [
    "img = cv2.imread(\"messi5.jpg\")\n",
    "%timeit res = cv2.medianBlur(img,49)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:15.772176Z",
     "start_time": "2019-02-25T08:58:15.766179Z"
    }
   },
   "outputs": [],
   "source": [
    "# 设置优化器的开关\n",
    "cv2.setUseOptimized(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:15.978059Z",
     "start_time": "2019-02-25T08:58:15.962067Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cv2.useOptimized()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:20.812289Z",
     "start_time": "2019-02-25T08:58:18.279743Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "30.9 ms ± 448 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit res = cv2.medianBlur(img,49)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看出使用优化器前后的运行性能"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### IPython 性能测试\n",
    "测试一下下面几个计算平方的方法哪个更好。\n",
    "例子：\n",
    "`x = 5; y = x**2`、`x = 5; y = x*x`、`x = np.uint8([5]); y = x*x`、`y = np.square(x)` 可以使用 `%timeit` 进行测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:22.585439Z",
     "start_time": "2019-02-25T08:58:22.581441Z"
    }
   },
   "outputs": [],
   "source": [
    "x = 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:27.188803Z",
     "start_time": "2019-02-25T08:58:23.041179Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "505 ns ± 18 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit y = x**2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:33.815006Z",
     "start_time": "2019-02-25T08:58:27.279752Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "77.5 ns ± 1.98 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit y = x*x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:39.659661Z",
     "start_time": "2019-02-25T08:58:33.914950Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "709 ns ± 57.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
     ]
    }
   ],
   "source": [
    "z = np.uint8([5])\n",
    "%timeit y=z*z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:45.435372Z",
     "start_time": "2019-02-25T08:58:39.772596Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "674 ns ± 66.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit y=np.square(z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`y = x*x` 比 Numpy 运算快不少\n",
    "\n",
    "Python 标量操作比 Numpy 标量操作快。因此对于包含一个或两个元素的操作，Python 标量优于 Numpy 数组。当数组的大小稍微大一点时，Numpy 就更有优势。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:47.847971Z",
     "start_time": "2019-02-25T08:58:45.526301Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "269 µs ± 30.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
     ]
    }
   ],
   "source": [
    "# 比较 cv2.countNonZero() 和 np.count_nonzero() 的性能\n",
    "%timeit z = cv2.countNonZero(img[:,:, 0]) # 该函数只能计算单通道的图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-25T08:58:54.551175Z",
     "start_time": "2019-02-25T08:58:49.070318Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "677 µs ± 54.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit z = np.count_nonzero(img[:,:, 0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 性能优化技术\n",
    "首先尝试以一种简单的方式实现该算法。一旦开始工作，对其进行分析，找到瓶颈并对其进行优化。\n",
    "- 尽量避免循环，特别是 2/3 重的循环\n",
    "- 最大限度地向量化算法/代码，因为 Numpy 和 OpenCV 是针对向量操作进行优化的。\n",
    "- 利用缓存一致性\n",
    "- 除非必要，不然尽量不要对数组进行复制，数组复制的开销很大\n",
    "\n",
    "即使在执行了所有这些操作之后，如果代码仍然很慢，或者不可避免地要使用大型循环，那么可以使用其他库，比如Cython来加快速度。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 更多资料\n",
    "[Python Optimization Techniques]\n",
    "(https://wiki.python.org/moin/PythonSpeed/PerformanceTips)、\n",
    "Scipy Lecture Notes - [Advanced Numpy]\n",
    "(http://scipy-lectures.org/advanced/advanced_numpy/index.html#advanced-numpy)\n",
    "time profile 模块"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "hide_input": false,
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "235.99px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
